<template>
  <div id="gear-container">
    <div id="gear-param">
      <el-form :model="form" :inline="true">
        <el-form-item label="SIZE">
          <el-input v-model="form.size.x"></el-input>
          <el-input v-model="form.size.y"></el-input>
          <el-input v-model="form.size.z"></el-input>
        </el-form-item>
        <el-form-item>
          <el-checkbox v-model="form.borderChecked" border @click.native="doChange">边框</el-checkbox>
        </el-form-item>
        <el-form-item label="Extra">
          <el-select v-model="form.extra" placeholder="请选择" @click.native.stop @change="extraChange">
            <el-option
              v-for="item in extraOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onSubmit">生效</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div id="gear-canvas"></div>
  </div>
</template>

<script>
  import * as THREE from 'three'
  import OrbitControls from 'threejs-orbit-controls'

  export default {
    name: "GearWheel3D",
    data() {
      return {
        form: {
          size: {
            x: 1,
            y: 1,
            z: 0.5
          },
          borderChecked: false,
          extra: 0
        },
        extraOptions: [],
        gear3D: null,
        lineSegments: null,
        scene: null

      }
    },
    methods: {
      extraChange(extra) {
        this.scene.remove(this.gear3D)
        this.gear3D = null
        this.createGear(extra)
      },
      onSubmit() {
        this.gear3D.scale.x = this.form.size.x
        this.gear3D.scale.y = this.form.size.y
        this.gear3D.scale.z = this.form.size.z
      },
      doChange(val) {
        this.form.borderChecked = !this.form.borderChecked
        if (this.form.borderChecked) {
          this.addBorderLine()
        } else {
          this.gear3D.remove(this.lineSegments)
          this.lineSegments = null
        }
      },
      addBorderLine() {
        let cubeEdges = new THREE.EdgesGeometry(this.gear3D.geometry, 45);
        let edgesMtl = new THREE.LineBasicMaterial({color: 0xff0000});
        edgesMtl.depthTest = false
        this.lineSegments = new THREE.LineSegments(cubeEdges, edgesMtl);
        this.gear3D.add(this.lineSegments)

      },
      innerGearShape(eachAngle, gear_number, rf, ra) {
        const points = []
        for (let i = 0; i < gear_number; i++) {
          const p0 = new THREE.Vector2(rf * Math.cos(eachAngle / 4 + i * eachAngle), rf * Math.sin(eachAngle / 4 + i * eachAngle))
          const p1 = new THREE.Vector2(ra * Math.cos(eachAngle / 2 + i * eachAngle), ra * Math.sin(eachAngle / 2 + i * eachAngle))
          const p2 = new THREE.Vector2(ra * Math.cos(-eachAngle / 4 + i * eachAngle), ra * Math.sin(-eachAngle / 4 + i * eachAngle))
          const p3 = new THREE.Vector2(rf * Math.cos(0 + i * eachAngle), rf * Math.sin(0 + i * eachAngle))
          points.push([p0, p1, p2, p3])

        }


        var shape = new THREE.Shape()
        var hole = new THREE.Path();
        const point_len = points.length

        shape.moveTo(points[0][1].x, points[0][1].y)

        shape.lineTo(points[0][0].x, points[0][0].y)

        shape.lineTo(points[0][3].x, points[0][3].y)
        shape.lineTo(points[0][2].x, points[0][2].y)
        shape.lineTo(points[0][1].x, points[0][1].y)
        hole.moveTo(points[0][0].x, points[0][0].y)
        hole.lineTo(points[0][1].x, points[0][1].y)
        for (let i = 1; i < point_len; i++) {
          shape.lineTo(points[i][2].x, points[i][2].y)
          shape.lineTo(points[i][1].x, points[i][1].y)
          shape.lineTo(points[i][0].x, points[i][0].y)
          shape.lineTo(points[i][3].x, points[i][3].y)
          //保证逆时针方向 解决奇数个齿轮 渲染不全的情况不再发生
          shape.lineTo(points[i][2].x, points[i][2].y)
          shape.lineTo(points[i][3].x, points[i][3].y)
          shape.lineTo(points[i][0].x, points[i][0].y)
          shape.lineTo(points[i][1].x, points[i][1].y)
        //  shape.moveTo(points[i][1].x, points[i][1].y)

          hole.lineTo(points[i][2].x, points[i][2].y)
          hole.lineTo(points[i][3].x, points[i][3].y)
          hole.lineTo(points[i][0].x, points[i][0].y)
          hole.lineTo(points[i][1].x, points[i][1].y)
          if (point_len - 1 === i) {
            shape.lineTo(points[0][2].x, points[0][2].y)
            hole.lineTo(points[0][2].x, points[0][2].y)
            hole.lineTo(points[0][3].x, points[0][3].y)
            hole.lineTo(points[0][0].x, points[0][0].y)
          }
        }
        shape.holes.push(hole)

        return shape
      },
      outterGearShape(eachAngle, gear_number, rf, ra) {
        const holeRadiusPercent = 0.95
        const points = []
        for (let i = 0; i < gear_number; i++) {
          const p0 = new THREE.Vector2(rf * Math.cos(3 * eachAngle / 8 + i * eachAngle), rf * Math.sin(3 * eachAngle / 8 + i * eachAngle))
          const p1 = new THREE.Vector2(ra * Math.cos(eachAngle / 8 + i * eachAngle), ra * Math.sin(eachAngle / 8 + i * eachAngle))
          const p2 = new THREE.Vector2(ra * Math.cos(-eachAngle / 8 + i * eachAngle), ra * Math.sin(-eachAngle / 8 + i * eachAngle))
          const p3 = new THREE.Vector2(rf * Math.cos(-3 * eachAngle / 8 + i * eachAngle), rf * Math.sin(-3 * eachAngle / 8 + i * eachAngle))
          points.push([p0, p1, p2, p3])

        }


        var shape = new THREE.Shape()
        var hole = new THREE.Path();
        const point_len = points.length
        const first_point = points[0][0]
        shape.moveTo(first_point.x, first_point.y)
        shape.lineTo(points[0][3].x, points[0][3].y)
        hole.moveTo(holeRadiusPercent * points[0][3].x, holeRadiusPercent * points[0][3].y)
        shape.lineTo(points[0][2].x, points[0][2].y)
        shape.lineTo(points[0][1].x, points[0][1].y)
        shape.lineTo(first_point.x, first_point.y)
        hole.lineTo(holeRadiusPercent * first_point.x, holeRadiusPercent * first_point.y)
        for (let i = 1; i < point_len; i++) {
          shape.lineTo(points[i][3].x, points[i][3].y)
          hole.lineTo(holeRadiusPercent * points[i][3].x, holeRadiusPercent * points[i][3].y)
          shape.lineTo(points[i][2].x, points[i][2].y)
          shape.lineTo(points[i][1].x, points[i][1].y)
          shape.lineTo(points[i][0].x, points[i][0].y)
          hole.lineTo(holeRadiusPercent * points[i][0].x, holeRadiusPercent * points[i][0].y)
          shape.lineTo(points[i][3].x, points[i][3].y)
          shape.moveTo(points[i][0].x, points[i][0].y)
          if (point_len - 1 === i) {
            shape.lineTo(points[0][3].x, points[0][3].y)
            hole.lineTo(holeRadiusPercent * points[0][3].x, holeRadiusPercent * points[0][3].y)
          }
        }
        shape.holes.push(hole)
        return shape
      },
      createGear(extra) {
        const innerGear = extra < 0
        const gear_number = Math.abs(extra) >= 0 && Math.abs(extra) <= 3 ? 3 : Math.abs(extra);
        const radius = 10;
        const width = 2 * radius;
        const d = width / 2//分度圆径
        const m = d / gear_number//模数
        const ra = m * (gear_number + 2) / 2 //齿顶圆直径
        const rf = m * (gear_number - 2.5) / 2 //齿根圆直径
        const eachAngle = 2 * Math.PI / gear_number

        let shape = innerGear ? this.innerGearShape(eachAngle, gear_number, rf, ra) : this.outterGearShape(eachAngle, gear_number, rf, ra)

        var extrudeSettings = {
          amount: 10,
          bevelEnabled: false,
          bevelSegments: 8,
          steps: 50,
          bevelSize: 1,
          bevelThickness: 1,
          depth: 400
        };

        var pipegeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

        var material = new THREE.MeshNormalMaterial({
          vertexColors: THREE.VertexColors,
          flatShading: true
        });
        this.gear3D = new THREE.Mesh(pipegeometry, material);


        this.scene.add(this.gear3D)
      }
    },
    mounted() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0xf0f0f0)
      var axes = new THREE.AxesHelper(100)
      this.scene.add(axes)
      var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10000);
      //  var camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 1000 );
      var controls = new OrbitControls(camera, document.getElementById('gear-canvas'));

      var raycaster = new THREE.Raycaster();
      var mouse = new THREE.Vector2();

      var renderer = new THREE.WebGLRenderer();


      renderer.setSize(4 * window.innerWidth / 5, window.innerHeight);
      document.getElementById('gear-canvas').appendChild(renderer.domElement)

      this.createGear(0)

      camera.position.set(20, 10, 150);
      controls.update();

      const _this = this
      var animate = function () {
        requestAnimationFrame(animate);
        controls.update();
        renderer.render(_this.scene, camera);
      };

      function onMouseMove(event) {
        event.preventDefault();
        mouse.x = ((event.clientX - renderer.domElement.getBoundingClientRect().left) / renderer.domElement.offsetWidth) * 2 - 1;
        mouse.y = -((event.clientY - renderer.domElement.getBoundingClientRect().top) / renderer.domElement.offsetHeight) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);

        // 计算物体和射线的焦点
        let intersects = raycaster.intersectObjects(this.scene.children);
        for (var i = 0; i < intersects.length; i++) {
          intersects[i].object.material.color.set(0x6699ff);
        }
      }

    //  window.addEventListener('click', onMouseMove, false);
      requestAnimationFrame(animate)
    },
    created() {
      for (let i = -13; i <= 13; i++) {
        this.extraOptions.push({
          "label": i, "value": i
        })
      }
    }
  }
</script>

<style scoped>
  .el-form-item__content {
    display: flex;
  }

  input {
    width: 20px;
  }
</style>
